# 线性模型

'''
 线性回归模型在不同维度下的表现可以概括为：
 对于单一特征，模型预测是一条直线；
 对于两个特征，是平面；
 对于更多特征，则是一个超平面。
'''
import mglearn
from sklearn.model_selection import train_test_split

'''
 比较直线预测和KNeighborsRegressor的结果，我们发现直线预测丢失了很多数据细节，能力有限。
 这种局限源于一个强假设：目标变量完全是特征的线性组合，这在现实中可能不成立。
 然而，这种看法在一维数据中尤其明显，而在多特征数据集中，线性模型可以非常有效，
 尤其是在特征数量超过样本数量时，理论上可以完美拟合任何目标变量。

 线性回归模型的不同之处在于它们如何确定参数以及控制模型的复杂度。接下来，我们将介绍一些最常见的线性回归模型。
'''

'''
 线性回归，也称为普通最小二乘法（OLS），是一种基本的线性回归技术。
 它通过寻找参数w和b来最小化训练集中预测值与实际值y的均方误差（MSE）。
 MSE是预测误差平方和的平均值。线性回归简单易用，但无法调节模型复杂度。
'''

from sklearn.linear_model import LinearRegression

X, y = mglearn.datasets.make_wave(n_samples=60)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
lr = LinearRegression().fit(X_train, y_train)

# “斜率”参数（w，也叫作权重或系数）被保存在 coef_ 属性中，
# 而偏移或截距（b）被保存在 intercept_ 属性中：
print(f"lr.coef_: {lr.coef_}")
print(f"lr.intercept_: {lr.intercept_}")
# lr.coef_: [0.39390555]
# lr.intercept_: -0.031804343026759746

'''
 coef_ 和 intercept_ 后面的下划线是 scikit-learn 的约定，用来区分训练结果和用户设定的参数。
 intercept_ 是一个浮点数，代表模型的截距；
 coef_ 是一个 NumPy数组，包含每个特征的权重。
 如果数据集只有一个特征，coef_ 中就只有一个元素。
'''

# 看一下训练集和测试集的性能
print("Training set score: {:.2f}".format(lr.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lr.score(X_test, y_test)))

'''
R²值大约是0.66，不算理想，但训练集和测试集分数接近，暗示模型可能欠拟合而非过拟合。
在一维数据集上，过拟合风险小，因为模型简单。
但在高维数据集上，线性模型更强大，过拟合风险也更高。
'''

# 下面在更复杂的数据集上观察其表现，比如波士顿的房价数据集
X, y = mglearn.datasets.load_extended_boston()
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
lr = LinearRegression().fit(X_train, y_train)

# 比较一下训练集和测试集的分数就可以发现，我们在训练集上的预测非常准确，但测试集上的 R² 要低很多：
print("Training set score: {:.2f}".format(lr.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lr.score(X_test, y_test)))
# Training set score: 0.95
# Test set score: 0.61

'''
训练集和测试集表现不一致通常是过拟合的迹象，所以我们需要一个能控制模型复杂度的方法。
岭回归是线性回归的一个常用替代方案，用以减少过拟合。
'''
